package com.ycxh.interceptor;

import com.ycxh.util.ErrorMessageEnum;
import com.ycxh.util.ViewResult;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

/**
 * 访问权限拦截器
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {

    private static Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);

    @Value("${IP.whitelist:''}")
    String ipWhiteList;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        logger.info("Interceptor-preHandle");
        getIpAddress(request);

        // 限制访问ip
        if (StringUtils.isNotBlank(ipWhiteList)) {
            // 问题：查询本地IP地址的时候显示的是IPv6的0:0:0:0:0:0:0:1
            // https://www.cnblogs.com/cheng-/p/14297033.html
            // https://www.cnblogs.com/recorderM/p/15974319.html
            // 解决方式： 将localhost换成127.0.0.1访问，因为电脑优先把localhost解析成了IPv6
            // http://localhost:8088/deploy/ 0:0:0:0:0:0:0:1
            // http://127.0.0.1:8088/deploy/ 127.0.0.1
            String ipAddress = getIpAddress(request);

            if (StringUtils.isBlank(ipAddress) || !Arrays.asList(ipWhiteList.split(",")).contains(ipAddress)) {
                logger.info("ip不在白名单，无权访问，ip：" + (ipAddress == null ? "" : ipAddress));
                // 中文乱码
                response.setContentType("text/javascript; charset=utf-8");
                response.getWriter().write(ViewResult.newInstance().code(ErrorMessageEnum.用户无操作权限.getCode()).msg(ErrorMessageEnum.用户无操作权限.getMsg()).json());
                return false;
            }
        }

        // springboot中使用session进行登录验证
        // https://www.dandelioncloud.cn/article/details/1498207299536756737
        // 登录页、登录接口或静态资源js、css等放行
        // /deploy/login 会带上项目名context-path
        String requestUri = request.getRequestURI();
        // http://127.0.0.1:8088/deploy/login
        // String requestUrl = request.getRequestURL();
        // /deploy 未指定时为空字符串
        // 如果不需要拦截所有请求，那么不应该写在这里，应该在注册拦截器时排除
        // 现在要判断ip，所以继续写在这里
        String contextPath = request.getContextPath();
        if (requestUri.startsWith(contextPath + "/login") || requestUri.startsWith(contextPath + "/loginJQ")
                || requestUri.startsWith(contextPath + "/user/login")
                || requestUri.startsWith(contextPath + "/other/")
                || requestUri.endsWith(".js") || requestUri.endsWith(".css")
                || requestUri.endsWith(".jpg") || requestUri.endsWith(".svg")
                || requestUri.endsWith(".map") || requestUri.endsWith(".gif")) {
            return true;
        }
        // 重定向
        Object object = request.getSession().getAttribute("user");
        if (object == null || !object.toString().equals("admin")) {
            // response.sendRedirect  https://blog.csdn.net/pq5326/article/details/327790
            // response中的sendRedirect和request中的转发使用区别 https://www.cnblogs.com/luckforefforts/p/13642679.html

            // 相对路径会补上项目名（/deploy/login），绝对路径则不会（/login）
            // response.sendRedirect("/login");

            // 下载文件未登录跳转登录页错误，所以还是手动拼上项目名吧
            // http://127.0.0.1:8088/deploy/file/downloadFile/login
            // response.sendRedirect("login");
            response.sendRedirect(contextPath + "/login");
            return false;
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        // logger.info("Interceptor-postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        // logger.info("Interceptor-afterCompletion");
    }

    /**
     * 获取用户访问ip地址
     * https://blog.csdn.net/hongchen006/article/details/122940205
     */
    public String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }

        // natapp(ngrok) 内网穿透后取得访客真实IP https://natapp.cn/article/client_real_ip
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            // natapp专属 请求头
            ip = request.getHeader("X-Natapp-Ip"); // 公网ip
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            // 访客的真实IP地址
            ip = request.getHeader("X-Real-Ip"); // 127.0.0.1
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 获取到多个ip时取第一个作为客户端真实ip
        if (StringUtils.isNotEmpty(ip) && ip.contains(",")) {
            String[] ipArray = ip.split(",");
            if (ArrayUtils.isNotEmpty(ipArray)) {
                ip = ipArray[0];
            }
        }
        return ip;
    }

}
